// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

#include "stdafx.h"
#include "ShaderManager.h"
#include "ShaderProgram.h"
#include "ShaderEvent.h"

// - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

ShaderManager::ShaderManager()
: m_time(0.f)
{
}

ShaderManager::~ShaderManager()
{
}

// - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

void ShaderManager::registerShader(ref<ShaderProgram> program, 
                                   int flags)
{
    // If a Shader by that name exists, don't do anything
    if(getShaderByName(program->getName()) != NULL)
    {
        ASSERT(false);
        return;
    }

    // If a Shader by that id exists, don't do anything
    if(getShaderById(program->getId()) != NULL)
    {
        ASSERT(false);
        return;
    }

    program->setManager(this);
    m_shaders.insert(program);

    program->setManager(this);

    // Register the shader as a timed shader as well in case 
    // the corresponding flag is raised
    if((flags & IsTimedShader) != 0)
    {
        m_timedShaders.insert(program.get());
    }
    
    ListenerIt it = m_listeners.begin();
    for(; it != m_listeners.end(); ++it)
    {
        ShaderListener* listener = *it;
        ShaderEvent event_(ShaderEvent::ShaderProgramAdded);
        event_.sender = this;
        event_.program = program.get();
        listener->onShaderEvent(event_);
    }
}


std::wstring ShaderManager::generateNameFromPrefix(const std::wstring& prefix) const
{
    for(int i = 0; ; ++i) // Will eventually find an unused name
    {
        std::wostringstream oss;
        oss << prefix << i;
        if(getShaderByName(oss.str()) == NULL)
        {
            return oss.str();
        }
    }
}

ShaderProgram* ShaderManager::getShaderById(int id) const
{
    // TODO this map should be the other way around for efficiency
    for(ShaderIt it = m_shaders.begin(); it != m_shaders.end(); ++it)
    {
        if((*it)->getId() == id)
        {
            return it->get();
        }
    }
    return NULL;
}

ShaderProgram* ShaderManager::getShaderByName(const std::wstring& name) const
{
    // TODO this map should be the other way around for efficiency
    for(ShaderIt it = m_shaders.begin(); it != m_shaders.end(); ++it)
    {
        if((*it)->getName() == name)
        {
            return it->get();
        }
    }
    return NULL;
}

// - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

void ShaderManager::clear()
{
    m_shaders.clear();
}

// - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

ShaderManager::ShaderIt ShaderManager::beginShaders() const
{
    return m_shaders.begin();
}

ShaderManager::ShaderIt ShaderManager::endShaders() const
{
    return m_shaders.end();
}


size_t ShaderManager::getShaderCount() const
{
    return m_shaders.size();
}

// - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

void ShaderManager::addShaderListener(ShaderListener* listener)
{
    ASSERT(m_listeners.find(listener) == m_listeners.end());
    m_listeners.insert(listener);
}

void ShaderManager::removeShaderListener(ShaderListener* listener)
{
    ASSERT(m_listeners.find(listener) != m_listeners.end());
    m_listeners.erase(listener);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
void ShaderManager::notifyShaderEvent(ShaderEvent& event_)
{
    event_.sender = this;
    ListenerIt it = m_listeners.begin();
    for(; it != m_listeners.end(); ++it)
    {
        ShaderListener* listener = *it;
        listener->onShaderEvent(event_);
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

void ShaderManager::onBeginRender(View3d*)
{
    std::set<ShaderProgram*>::iterator it = m_timedShaders.begin();
    for(; it != m_timedShaders.end(); ++it)
    {
        ShaderProgram* shader = *it;
        shader->setUniformVar("time", m_time);
    }
    m_time += .01f;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

bool ShaderManager::needsContinuousRender(View3d*)
{
    return false;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

ref<ShaderProgram> ShaderManager::simpleLoad(const std::string& fileName,
                                             int flags)
{
    ref<ShaderProgram> shader(new ShaderProgram);
    shader->addVertexShader(fileName + ".vert");
    shader->addFragmentShader(fileName + ".frag");
    CA2T unicode(fileName.c_str());
    shader->setName(std::wstring(unicode));
    registerShader(shader, flags);
    return shader;
}